package com.aidex.biz.service.impl;

import com.aidex.biz.domain.*;
import com.aidex.biz.service.*;
import com.aidex.common.config.AiDexConfig;
import com.aidex.common.core.domain.R;
import com.aidex.common.core.domain.entity.Company;
import com.aidex.common.utils.SecurityUtils;
import com.aidex.common.utils.StringUtils;
import com.aidex.common.utils.sms.ChuangLanSmsClient;
import com.aidex.system.service.CompanyService;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.exception.HttpCodeException;
import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import com.wechat.pay.contrib.apache.httpclient.util.RsaCryptoUtil;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.RSAConfig;
import com.wechat.pay.java.service.certificate.CertificateService;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.payments.jsapi.model.Payer;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.AmountReq;
import com.wechat.pay.java.service.refund.model.CreateRequest;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.crypto.IllegalBlockSizeException;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.List;

/**
 * 微信商户支付信息Service业务层处理
 *
 * @author sean
 * @email 2074844820@qq.com
 * @date 2022-09-21
 */
@Service
public class WxPayServiceImpl implements WxPayService {
    private static final Logger log = LoggerFactory.getLogger(WxPayServiceImpl.class);

    @Autowired
    private UserTixianService userTixianService;
    @Autowired
    private CompanyService companyService;
    @Autowired
    private SmSSendService smSSendService;
    @Autowired
    private AccountInfoService accountInfoService;
    @Autowired
    private AccountLogService accountLogService;

    @Autowired
    private UserInfoService userInfoService;

    public static  JsapiServiceExtension service=null;
    public static RefundService refundService=null;
    private static Config config=null;


    /**
     * 用户提现 -微信打款
     *
     * @param userTixian
     */
    @Override
    public boolean transferBatches(UserTixian userTixian) {

        // 获取租户配置
        Company company = companyService.get(AiDexConfig.getCompanyId());

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("appid", company.getWechatXappid());
        jsonObject.put("out_batch_no", StringUtils.Getnum());
        jsonObject.put("batch_name", company.getXcxName() + "-商家提现");
        jsonObject.put("batch_remark", company.getXcxName());
        jsonObject.put("total_amount", userTixian.getMoney().multiply(new BigDecimal(100)));
        jsonObject.put("total_num", 1);

        JSONArray rootNodeList = new JSONArray();
        JSONObject jsonObjectDetails = new JSONObject();
        jsonObjectDetails.put("out_detail_no", userTixian.getId());
        jsonObjectDetails.put("transfer_amount", jsonObject.get("total_amount"));
        jsonObjectDetails.put("transfer_remark",
                company.getXcxName() + "商家提现" + "(" + userTixian.getName() + "/" + userTixian.getPhone() + ")");
        jsonObjectDetails.put("openid", userTixian.getUserInfoId());
        // 大于=2000的时候必须验证用户名
        if (userTixian.getMoney().compareTo(new BigDecimal(2000)) >= 0) {
            jsonObjectDetails.put("user_name", ciphertContent(company.getWechatMchid().trim(),
                    company.getCertNo().trim(), company.getWechatMchKey().trim(), company.getCertKey().trim(),
                    userTixian.getName().trim()));
        }
        rootNodeList.add(jsonObjectDetails);
        jsonObject.put("transfer_detail_list", rootNodeList);
        // 微信发起转账
        String result = wxpayUserChange(company.getWechatMchid().trim(), company.getCertNo().trim(),
                company.getWechatMchKey().trim(), company.getCertKey().trim(), jsonObject);
        JSONObject resultJson = JSONObject.parseObject(result);
        if (StringUtils.isEmpty(resultJson.getString("batch_id"))) {
            // 转账失败
            userTixian.setStatus("99");
            userTixian.setRemark(resultJson.getJSONObject("error").getString("message"));
        } else {
            // 修改提现状态 转账中
            userTixian.setStatus("1");
            userTixian.setWxPayNo(resultJson.getString("out_batch_no") + "/" + resultJson.getString("batch_id"));
        }
        return userTixianService.save(userTixian);
    }

    /**
     * 微信转账到零钱
     *
     * @param merchantId
     * @param merchantSerialNumber
     * @param apiV3Key
     * @param privateKeyUrl
     * @param jsonObject
     * @return
     */
    @Override
    public String wxpayUserChange(String merchantId, String merchantSerialNumber, String apiV3Key,
            String privateKeyUrl, JSONObject jsonObject) {
        String result = "";
        PrivateKey merchantPrivateKey = null;
        InputStream inputStream = null;
        try {
            URL url = new URL(privateKeyUrl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(5 * 1000);
            inputStream = conn.getInputStream();//获取远程文件流
        } catch (IOException e) {
            e.printStackTrace();
        }
        merchantPrivateKey = PemUtil.loadPrivateKey(inputStream);
        // 获取证书管理器实例
        CertificatesManager certificatesManager = CertificatesManager.getInstance();
        // 向证书管理器增加需要自动更新平台证书的商户信息
        try {
            try {
                certificatesManager.putMerchant(merchantId, new WechatPay2Credentials(merchantId,
                        new PrivateKeySigner(merchantSerialNumber, merchantPrivateKey)),
                        apiV3Key.getBytes(StandardCharsets.UTF_8));
            } catch (GeneralSecurityException e) {
                e.printStackTrace();
            } catch (HttpCodeException e) {
                e.printStackTrace();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 从证书管理器中获取verifier
        Verifier verifier = null;
        try {
            verifier = certificatesManager.getVerifier(merchantId);
        } catch (NotFoundException e) {
            e.printStackTrace();
        }
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create().withMerchant(merchantId,
                merchantSerialNumber, merchantPrivateKey).withValidator(new WechatPay2Validator(verifier));
        // 通过WechatPayHttpClientBuilder构造的HttpClient，会自动的处理签名和验签，并进行证书自动更新
        CloseableHttpClient httpClient = builder.build();
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/transfer/batches");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");
        // 后面跟使用Apache HttpClient一样
        try {
            httpPost.setEntity(new StringEntity(jsonObject.toJSONString(), "UTF-8"));
            CloseableHttpResponse response = httpClient.execute(httpPost);
            int status = response.getStatusLine().getStatusCode();
            HttpEntity entity = response.getEntity();
            //转为字符串
            if (status == HttpStatus.SC_OK) {
                result = EntityUtils.toString(entity, "UTF-8");
            } else {
                JSONObject returnjson = new JSONObject();
                returnjson.put("status", status);
                returnjson.put("error", EntityUtils.toString(entity, "UTF-8"));
                result = returnjson.toJSONString();
            }
            return result;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 敏感信息加密
     *
     * @param merchantId
     * @param merchantSerialNumber
     * @param apiV3Key
     * @param privateKeyUrl
     * @param content
     * @return
     */
    @Override
    public String ciphertContent(String merchantId, String merchantSerialNumber, String apiV3Key,
            String privateKeyUrl, String content) {
        String result = "";
        PrivateKey merchantPrivateKey = null;

        InputStream inputStream = null;

        try {
            URL url = new URL(privateKeyUrl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(5 * 1000);
            inputStream = conn.getInputStream();//获取远程文件流
        } catch (IOException e) {
            e.printStackTrace();
        }
        merchantPrivateKey = PemUtil.loadPrivateKey(inputStream);
        // 获取证书管理器实例
        CertificatesManager certificatesManager = CertificatesManager.getInstance();

        // 向证书管理器增加需要自动更新平台证书的商户信息
        try {
            try {
                certificatesManager.putMerchant(merchantId, new WechatPay2Credentials(merchantId,
                        new PrivateKeySigner(merchantSerialNumber, merchantPrivateKey)),
                        apiV3Key.getBytes(StandardCharsets.UTF_8));
            } catch (GeneralSecurityException e) {
                e.printStackTrace();
            } catch (HttpCodeException e) {
                e.printStackTrace();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 从证书管理器中获取verifier
        Verifier verifier = null;
        try {
            verifier = certificatesManager.getVerifier(merchantId);
        } catch (NotFoundException e) {
            e.printStackTrace();
        }
        try {
            if (!StringUtils.isEmpty(content)) {
                result = RsaCryptoUtil.encryptOAEP(content, verifier.getValidCertificate());

            }

        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        }
        return result;
    }

    @Override
    public String transferBatchesOutDetailNo(String merchantId, String merchantSerialNumber, String apiV3Key,
            String privateKeyUrl, JSONObject jsonObject) {
        String result = "";
        PrivateKey merchantPrivateKey = null;
        InputStream inputStream = null;
        try {
            URL url = new URL(privateKeyUrl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(5 * 1000);
            inputStream = conn.getInputStream();//获取远程文件流
        } catch (IOException e) {
            e.printStackTrace();
        }
        merchantPrivateKey = PemUtil.loadPrivateKey(inputStream);
        // 获取证书管理器实例
        CertificatesManager certificatesManager = CertificatesManager.getInstance();
        // 向证书管理器增加需要自动更新平台证书的商户信息
        try {
            try {
                certificatesManager.putMerchant(merchantId, new WechatPay2Credentials(merchantId,
                        new PrivateKeySigner(merchantSerialNumber, merchantPrivateKey)),
                        apiV3Key.getBytes(StandardCharsets.UTF_8));
            } catch (GeneralSecurityException e) {
                e.printStackTrace();
            } catch (HttpCodeException e) {
                e.printStackTrace();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 从证书管理器中获取verifier
        Verifier verifier = null;
        try {
            verifier = certificatesManager.getVerifier(merchantId);
        } catch (NotFoundException e) {
            e.printStackTrace();
        }
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create().withMerchant(merchantId,
                merchantSerialNumber, merchantPrivateKey).withValidator(new WechatPay2Validator(verifier));
        // 通过WechatPayHttpClientBuilder构造的HttpClient，会自动的处理签名和验签，并进行证书自动更新
        CloseableHttpClient httpClient = builder.build();
        HttpGet httpGet =
                new HttpGet("https://api.mch.weixin.qq.com/v3/transfer/batches/out-batch-no/" + jsonObject.getString(
                        "out_batch_no") + "/details" + "/out-detail-no/" + jsonObject.getString("out_detail_no"));
        // 后面跟使用Apache HttpClient一样
        try {
            httpGet.addHeader("Accept", "application/json");
            httpGet.addHeader("Content-type", "application/json; charset=utf-8");
            CloseableHttpResponse response = httpClient.execute(httpGet);
            int status = response.getStatusLine().getStatusCode();
            HttpEntity entity = response.getEntity();
            //转为字符串
            if (status == HttpStatus.SC_OK) {
                result = EntityUtils.toString(entity, "UTF-8");
            } else {
                JSONObject returnjson = new JSONObject();
                returnjson.put("status", status);
                returnjson.put("error", EntityUtils.toString(entity, "UTF-8"));
                result = returnjson.toJSONString();
            }
            return result;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 微信订单支付
     * @param order
     * @return
     */
    @Override
    public R payOrder(Order order)   {
        // UserInfo userInfo = userInfoService.get(order.getUserInfoId());
        Company company = companyService.getRedisCompany(AiDexConfig.getCompanyId());
        try{
            URL url = new URL(company.getCertKey());
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(5 * 1000);
            InputStream inStream = conn.getInputStream();//
            PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(inStream);
            if(null==config){
                config =
                        new RSAAutoCertificateConfig.Builder()
                                .merchantId(company.getWechatMchid())
                                .privateKey(merchantPrivateKey)
                                .merchantSerialNumber(company.getCertNo())
                                .apiV3Key(company.getWechatMchKey()).build();
            }
            if(null==service){
                service = new JsapiServiceExtension.Builder().config(config).build();
            }

            // 跟之前下单示例一样，填充预下单参数
            PrepayRequest request = new PrepayRequest();
            Amount amount = new Amount();
            amount.setTotal(order.getPayAmount().multiply(new BigDecimal("100")).intValue());
            amount.setCurrency("CNY");
            request.setAmount(amount);
            request.setAppid(company.getWechatXappid());
            request.setMchid(company.getWechatMchid());
            request.setDescription("0".equals(order.getOrderType()) ? "洗护订单" : "商城订单");
            request.setNotifyUrl(company.getNotifyUrl());
            request.setOutTradeNo(order.getOrderId());
            Payer payer=new Payer();
            payer.setOpenid(order.getUserInfoId());
            request.setPayer(payer);
            // response包含了调起支付所需的所有参数，可直接用于前端调起支付
            PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(request);
            return R.data(response);
        }catch (Exception e){
            return R.data(e);
        }
    }

    @Override
    public R refundOrder(RefundOrder refundOrder) {
        Company company = companyService.get(AiDexConfig.getCompanyId());
        try{
            URL url = new URL(company.getCertKey());
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(5 * 1000);
            InputStream inStream = conn.getInputStream();//
            PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(inStream);
            if(null==config){
                config =
                        new RSAAutoCertificateConfig.Builder()
                                .merchantId(company.getWechatMchid())
                                .privateKey(merchantPrivateKey)
                                .merchantSerialNumber(company.getCertNo())
                                .apiV3Key(company.getWechatMchKey()).build();
            }
            if(null==service){
                refundService = new RefundService.Builder().config(config).build();
            }
            CreateRequest createRequest=new CreateRequest();
            AmountReq amountReq=new AmountReq();
            amountReq.setTotal(refundOrder.getRefundMoeny().multiply(new BigDecimal("100")).longValue());
            amountReq.setRefund(refundOrder.getRefundMoeny().multiply(new BigDecimal("100")).longValue());
            amountReq.setCurrency("CNY");
            createRequest.setOutRefundNo(refundOrder.getOrderId());
            createRequest.setOutTradeNo(refundOrder.getOrderId());
            createRequest.setReason("用户取消订单");
            createRequest.setAmount(amountReq);
            // response包含了调起支付所需的所有参数，可直接用于前端调起支付
            return  R.data(refundService.create(createRequest));
        }catch (Exception e){
            log.error("微信退款错误："+e.getLocalizedMessage());
            return R.fail(e.getLocalizedMessage());
        }
    }


    private static JSONObject param(Order order, String appId, String mchId, String notifyUrl) {


        // 微信统一下单请求对象
        JSONObject reqJSON = new JSONObject();
        reqJSON.put("out_trade_no", order.getOrderId());
        reqJSON.put("description", "0".equals(order.getOrderType()) ? "洗护订单" :
                "商城订单");
        reqJSON.put("notify_url", notifyUrl);
        JSONObject amount = new JSONObject();
        amount.put("total", order.getAmount().multiply(new BigDecimal("100")));
        amount.put("currency", "CNY");
        reqJSON.put("amount", amount);

        JSONObject sceneInfo = new JSONObject();
        sceneInfo.put("payer_client_ip", SecurityUtils.getIpaddr());
        reqJSON.put("scene_info", sceneInfo);

        reqJSON.put("appid", appId);
        reqJSON.put("mchid", mchId);

        JSONObject payer = new JSONObject();
        payer.put("openid", order.getUserInfoId());
        reqJSON.put("payer", payer);

        return reqJSON;
    }


    public static void main(String[] args) {
        try {

            URL url =
                    new URL("https://ly-edu-sys.oss-cn-beijing.aliyuncs.com/2022/11/12/apiclient_key20221112170607" + ".pem");

            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(5 * 1000);
            InputStream inStream = conn.getInputStream();//
            PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(inStream);
            System.out.println(merchantPrivateKey.getAlgorithm());

        } catch (Exception e) {

            e.printStackTrace();

        }

    }


    public void q1(UserTixian userTixian, Company company) {
        // 提现失败
        AccountInfo accountInfo = accountInfoService.get(userTixian.getAccountId());
        accountInfo.setAccount(accountInfo.getAccount().add(userTixian.getMoney()));
        accountInfoService.save(accountInfo);

        AccountLog accountLog = new AccountLog();
        accountLog.setId(StringUtils.Getnum());
        accountLog.setIsNewRecord();
        accountLog.setUserAccountId(accountInfo.getId());
        accountLog.setMoney(userTixian.getMoney());
        accountLog.setType("提现失败-退回");
        accountLog.setStartMoney(accountInfo.getAccount().subtract(accountLog.getMoney()));
        accountLog.setEndMoney(accountInfo.getAccount().add(accountLog.getMoney()));
        accountLog.setTargetAccountId(accountInfo.getId());
        accountLog.setCompanyId(company.getId());
        accountLogService.save(accountLog);

        if ("1".equals(company.getSmsIsopen())) {
            ChuangLanSmsClient.sendSms(userTixian.getPhone(),
                    "尊敬的" + userTixian.getPhone() + "用户，您提现失败!请联系平台！");
        }
        // 提现成功
        JSONObject jsonObject = new JSONObject();
        userTixian.setWxPayNo(jsonObject.getString("out_batch_no") + "/" + jsonObject.getString("batch_id"));
        userTixian.setPayTime(new Date());
        if ("1".equals(company.getSmsIsopen())) {
//            smSSendService.sendSMS(company.getSmsBaoName(), company.getSmsBaoKey(), userTixian.getPhone(),
//                    company.getSmsqm(), "尊敬的" + userTixian.getPhone() + "用户，您提现成功，您的回收收益已到微信零钱，请到小程序查看");
            ChuangLanSmsClient.sendSms(userTixian.getPhone(),
                    "尊敬的" + userTixian.getPhone() + "用户，您提现成功，已到微信零钱，请到微信查看！");
        }
    }

    public void paytoend() {
        String merchantId = "1500342932";
        String merchantSerialNumber = "547FE2964B07C2B0FA56D22EB8C10B167266C83D";
        String apiV3Key = "21232f297a57a5a743894a0e4a801fc2";
        String privateKeyPath = "";
        /** 微信支付平台证书路径 */
        String wechatPayCertificatePath = "";
        Config config =
                new RSAConfig.Builder().merchantId(merchantId).privateKeyFromPath(privateKeyPath).merchantSerialNumber(merchantSerialNumber).wechatPayCertificatesFromPath(wechatPayCertificatePath).build();
        CertificateService certificateService = new CertificateService.Builder().config(config).build();
        List<X509Certificate> certificates =
                certificateService.downloadCertificate(apiV3Key.getBytes(StandardCharsets.UTF_8));
    }


}
